<?php
/**
 * kits folder class
 * used by webappkit.admin
 *
 * This file is part of Webappkit, a packages system for PHP web apps
 * @link http://webappkit.net
 * @package webappkit
 * @subpackage admin
 * @author J.Ducastel <nospam0@ducastel.name>
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */
class WakKitFolder extends WakFolder {

    /**
     * builds from kit id, not path
     * @param string $id kit id, not path !!!
     */
    public function __construct($id) {
        $this->_id = $id;
        $this->_path = Webappkit::getPathFromId($id);
        $this->_cfg = new WakKitCfg($id);
        if ($this->hasDocs())
            $this->docs = WakDocStorage::toolFactory($id);
    }

    public function __toString() {
        return 'kit folder '.$this->_id;
    }

    /**
     * get kit diagnostic
     * missing = folder not found
     * bad_cfg = missing or invalid wak.ini
     * bad_class = kti class is missing or invalid
     * missing_file = some loaded file missing
     * missing_kit = require missing kit(s)
     * missing_subkit = load missing subkit
     * bad_php = not compatible with current php version
     * ok = everything sweat
     * @return string
     */
    public function getStatus() {
        if (!$this->exists()) // folder doesn't exist
            return 'missing';
        if (!$this->_cfg->isValid()) // wak.ini don't exist or is not valid
            return 'bad_cfg';
        if (!$this->_checkClass()) // kit class do not exist or is not available
            return 'bad_class';
        if (!$this->_checkFiles())
            return 'missing_file';
        if (!$this->_checkDependencies())
            return 'missing_kit';
        // check subkits
        if (!$this->_checkPhp())
            return 'bad_php';
        return 'ok';
    }

    /**
     * creates kit if don't exist
     */
    public function create() {
        // create folder
        if (!parent::create())
            return false;
        // create wak.ini
        if (!$this->setIni(self::$_sample_cfg))
            return false;
        return true;
    }

    /**
     * is kit valid ?
     * = is well formed, has every listed files
     * @return bool
     */
    public function isValid() {
        return in_array($this->getStatus(),array('ok','missing_kit','bad_php'));
    }

    /**
     * is kit functional ?
     * status == ok
     * @return bool
     */
    public function isFunctional() {
        return $this->getStatus()=='ok';
    }

    /**
     * get kit id
     * @return string
     */
    public function getId() {
        return $this->_id;
    }

    /**
     * get kit name = last id segment
     * @return string
     */
    public function getName() {
        $parts=explode('.',$this->_id);
        return end($parts);
    }

    /**
     * @return string
     */
    public function getPath() {
        return $this->_path;
    }

    /**
     * get kit version
     * @return string
     */
    public function getVersion() {
        return $this->_cfg->getVersion();
    }

    /**
     * get kit description
     * @return string
     */
    public function getDescription() {
        return $this->_cfg->getDescription();
    }

    /**
     * get kit link if provided
     * @return string
     */
    public function getLink() {
        return $this->_cfg->getLink();
    }

    /**
     * get PHP compatibility range
     * @return string
     */
    public function getPhpRange() {
        return $this->_cfg->getPhpRange();
    }

    /**
     * get tools list
     * @return array name => class
     */
    public function listTools() {
        return $this->_cfg->getTools();
    }

    /**
     * get configuration in INI format
     * @return string
     */
    public function getIni() {
        $file=new WakIniFile($this->realPath().'/wak.ini');
        if (!$ini=$file->getContent()) {
            // return sample ini there
            return self::$_sample_cfg;
        }
        return $ini;
    }

    /**
     * get wak.ini file instance
     * @return WakIniFile
     */
    public function getIniFile() {
        return new WakIniFile($this->realPath().'/wak.ini');
    }

    /**
     * set Configuration
     * @param string $ini INI format
     * @return bool
     */
    public function setIni($ini) {
        $file=new WakIniFile($this->realPath().'/wak.ini');
        return $file->setContent($ini);
    }

    /**
     * get tool config in INI format
     * search for custom config, or return default
     * @param string $name tool's name
     * @return string
     */
    public function getToolIni($name) {
        if (!$this->_cfg->hasTool($name))
            return false;
        $file_path=$this->realPath().'/'.$name.'.tool.ini';
        // if config file exist, return its content
        if (file_exists($file_path)) {
            $file=new WakIniFile($file_path);
            return $file->getContent();
        }
        $class=$this->_cfg->getToolClass($name);
        $cfg=eval('return '.$class.'::$cfg;');
        return WakIniFile::arrayToIni($cfg);
        //$tool=new
        return 'default';
    }

    /**
     * saves tool config from INI format
     * @param string $name tool's name
     * @param string $ini
     * @return bool
     */
    public function setToolIni($name, $ini) {
        $file_path=$this->realPath().'/'.$name.'.tool.ini';
        $file=new WakIniFile($file_path);
        return $file->setContent($ini);
    }

    /**
     * list subkits
     * @return array n => name
     */
    public function listSubkits() {
        return Webappkit::listKits($this->_path);
    }

    /**
     * get subkits as WakKitFolder instances
     * @return array $name => WakKitFolder
     */
    public function getSubkits() {
        $list=$this->listSubkits();
        $kits=array();
        foreach ($list as $name) {
            $kits[$name]=new WakKitFolder($this->_id.'.'.$name);
        }
        return $kits;
    }

    /**
     * get dependencies list
     * = load_kits
     * @return array id => WakKitFolder
     */
    public function getDependencies() {
        $kits=array();
        foreach ($this->_cfg->getDependencies() as $id => $version) {
            // echo "<p>$id</p>";
            $kits[]=new WakKitFolder($id);
        }
        return $kits;
    }

    /**
     * list provided classes
     */
    public function getFiles() {
        return $this->_cfg->getFiles();
    }

    /**
     * list provided classes
     */
    public function getClasses() {
        return $this->_cfg->getClasses();
    }

    /**
     * does kit include unit tests ?
     * @return bool
     */
    public function hasTests() {
        $tests = $this->realPath().'/tests';
        return file_exists($tests);
        // return $this->_cfg->hasTool('tests');
    }

    /**
     * get tests report = run unit tests
     * @uses SimpleTest
     * @uses WakTestReporter
     * @return WakTestReporter
     */
    public function getTestsReport() {
        if (!$this->hasTests())
            return false;
        // loading kit libraries
        Webappkit::loadkit($this->_id);
        // including setup.php if present
        $setup_file = new WakFile($this->_path.'tests/setup.php'); // print_r($setup_file);
        if ($setup_file->exists()) {
            // trigger_error($setup_file->path());
            Webappkit::loadLibFile($setup_file->path());
        }
        // using SimpleTest
        $group=new GroupTest("tests for {$this->_id}");
        // listing files in tests/
        $folder = new WakFolder($this->_path.'tests');
        foreach ($folder as $file) {
            if (ereg('\.php$',$file) and !ereg('setup.php$',$file)) {
                // echo "<p>$file</p>";
                $group->addFile($this->_path.'tests/'.$file);
            } else if (ereg('\.php.$',$file)) {
                // running .php4, .php5 upon current PHP version
                if (substr($file, -1, 1) == substr(phpversion(),0,1)) {
                    // echo "<p>$file</p>";
                    $group->addFile($this->_path.'tests/'.$file);
                }
                // skipping next file
            }
        }
        // building reporter
        $this->reporter=new WakTestReporter("Tests for kit {$this->_id}");


        // executing setup.sql before tests
        $sql_file = new WakTextFile($this->_path.'tests/setup.sql');
        if ($sql_file->exists()) {

            // opening database connection
            $admin = Webappkit::getKit('webappkit.admin');
            $cfg = $admin->db_tests->getCfg(); $db = $cfg['dblink']['db'];
            if (!$admin->db_tests->connect()) {
                $this->reporter->paintFail("could not connect to tests database");
                return $this->reporter;
            } else {
                $this->reporter->paintMessage("opened connection to tests database ($db)");
                // opening connection with standard mysql library for more compatibility
                mysql_connect($cfg['dblink']['host'], $cfg['dblink']['user'], $cfg['dblink']['pwd']);
                mysql_select_db($cfg['dblink']['db']);
            }

            $sql = $sql_file->getContent();
            if (!$admin->db_tests->multiQuery($sql)) {
                $this->reporter->paintFail("could not execute setup.sql queries");
                $this->reporter->paintMessage($admin->db_tests->getError());
                $this->reporter->paintFormattedMessage($sql);
                return $this->reporter;
            }
            $this->reporter->paintMessage("executed queries from setup.sql");
        }

        // print_r($admin->db_tests);
        // running test cases
        $group->run($this->reporter);
        // executing teardown.sql before tests
        $sql_file = new WakTextFile($this->_path.'tests/teardown.sql');
        if ($sql_file->exists()) {
            $sql = $sql_file->getContent();
            if (!$admin->db_tests->multiQuery($sql)) {
                $this->reporter->paintFail("could not execute teardown.sql queries");
                return $this->reporter;
            }
            $this->reporter->paintMessage("executed queries from teardown.sql");
        }
        return $this->reporter;
        // echo '<pre>'; print_r($this->reporter);
        //return $kit->tests->run();
    }

    /**
     * sets locale (for docs)
     * @param string $locale en|fr etc
     * @return bool
     */
    public function setLocale($locale) {
        $this->_locale=$locale;
        if ($this->docs)
            $this->docs->setLocale($locale);
        return true;
    }

    /**
     * has docs ?
     * @return bool
     */
    public function hasDocs() {
        $docs=$this->realPath().'/docs';
        return file_exists($docs);
    }

    /**
     * list documentation files
     * @return array(iWakDocPage)
     */
    public function listDocPages() {
        return $this->docs->getAll();
    }

    /**
     * get a doc page by id
     * @param string $id
     * @return iWakDocPage
     */
    public function getDocPage($id) {
        return $this->docs->get($id);
    }

    /**
     * @var string kit id
     */
    protected $_id;

    /**
     * @var array kit config if available
     */
    protected $_cfg=array();

    /**
     * @var string current localisation settings
     */
    protected $_locale;

    protected static $_sample_cfg='[kit]
; kit-wide informations

desc = "a short description"
;version = "version string"
;class = "kit class, optional"

[load_kits]
; id = "version range"

[load_classes]
; class = "path"

[load_subkits]

[tools]
; name = "class"';

    /**
     * @var WakDocStorage docs storage if available
     */
    protected $docs;

    /**
     * check that kit class is available
     */
    protected function _checkClass() {
        return true;
        $class=$this->_cfg->getClass();
        return Webappkit::loadClass($class);
    }

    /**
     * check that listed files exist
     * @return bool
     */
    protected function _checkFiles() {
        foreach ($this->_cfg->getFiles() as $file) {
            if (!file_exists($this->_path.$this->_libRealFile($file)))
                return false;
        }
        foreach ($this->_cfg->getClasses() as $file) {
            if (!file_exists($this->_path.$this->_libRealFile($file)))
                return false;
        }
        return true;
    }

    /**
     * get real file name for a library file
     * = replace ending * by current php version
     */
    protected function _libRealFile($file) {
        if (substr($file,-1,1)=='*')
            $file=substr($file,0,-1).substr(phpversion(),0,1);
        // echo "<p>$file</p>";
        return $file;
    }

    /**
     * check kit dependencies
     */
    protected function _checkDependencies() {
        foreach ($this->_cfg->getDependencies() as $id => $version) {
            if (!Webappkit::checkKit($id))
                return false;
        }
        return true;
    }

    /**
     * check that kit is compatible with current php version
     */
    protected function _checkPhp() {
        return Webappkit::isVersionWithinRange(phpversion(),$this->_cfg->getPhpRange());
    }
}
